package com.tendory.rxlocation;

import android.content.Context;

import androidx.annotation.NonNull;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.Observer;
import io.reactivex.Single;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;


/**
 * 百度RxLocationManager的实现类.
 * Created by pitt on 2017/3/16.
 */
final class BaiduRxLocationManager implements RxLocationManager {
//    private static volatile BaiduRxLocationManager sINSTANCE;

    private volatile LocationClient mBdlocationClient;
    private ClientOption clientOption;

    private BaiduRxLocationManager(final Context context) {
        mBdlocationClient = new LocationClient(context.getApplicationContext());
    }

    @SuppressWarnings("PMD.NonThreadSafeSingleton")
    static BaiduRxLocationManager create(final Context context) {
        BaiduRxLocationManager sINSTANCE = null;
        if (sINSTANCE == null) {
            synchronized (BaiduRxLocationManager.class) {
                if (sINSTANCE == null) {
                    sINSTANCE = new BaiduRxLocationManager(context);
                }
            }
        }
        return sINSTANCE;
    }

    @Override
    public Observable<MyLocation> getLastLocation() {
        return Single.just(mBdlocationClient.getLastKnownLocation()).toObservable().compose(new Tras());
    }

    @Override
    public Observable<MyLocation> requestLocation() {
        final RxLocationListener rxLocationListener = new RxLocationListener(mBdlocationClient);
        return Observable.unsafeCreate(rxLocationListener).compose(new Tras());
    }

    private class Tras implements ObservableTransformer<BDLocation, MyLocation> {
        @Override
        public ObservableSource<MyLocation> apply(Observable<BDLocation> upstream) {
            return upstream.map(new Function<BDLocation, MyLocation>() {
                @Override
                public MyLocation apply(BDLocation bdLocation) throws Exception {
                    if (clientOption.isHandleError()) {
                        if (bdLocation == null) {
                            throw new MyLocationException("loc null");
                        }
                        int loctype = bdLocation.getLocType();
                        if (loctype == BDLocation.TypeServerError) {
                            throw new MyLocationException("服务端网络定位失败");
                            //"服务端网络定位失败，可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com，会有人追查原因"
                        }
                        if (loctype == BDLocation.TypeNetWorkException) {
                            throw new MyLocationException("网络不同导致定位失败，请检查网络是否通畅");
                        }
                        if (loctype == BDLocation.TypeCriteriaException) {
                            //processError("无法获取有效定位依据导致定位失败，一般是由于手机的原因，处于飞行模式下一般会造成这种结果，可以试着重启手机");
                            throw new MyLocationException("定位失败，一般原因有：飞行模式下、安全软件把该应用的定位权限禁用");
                        }
                    }
                    MyLocation savedLocation = new MyLocation();
                    savedLocation.coorType = bdLocation.getCoorType().equals("gcj02") ? MyLocation.COORTYPE_GCJ02 : MyLocation.COORTYPE_BD09;
                    savedLocation.locType = bdLocation.getLocType();//BDLocation.TypeGpsLocation BDLocation.TypeNetWorkLocation
                    savedLocation.time = bdLocation.getTime(); //获取定位时间
                    savedLocation.lon = bdLocation.getLongitude(); //获取经度信息
                    savedLocation.lat = bdLocation.getLatitude(); //获取纬度信息
                    savedLocation.direction = bdLocation.getDirection();//方向信息
                    //bdLocation.getAltitude();//获取海拔高度信息，单位米
                    savedLocation.acc = bdLocation.getRadius(); //获取定位精准度
                    savedLocation.add = bdLocation.getAddrStr();//获取地址信息
                    //city = bdLocation.getCity() == null ? null : bdLocation.getCity().replace("市", "");
                    savedLocation.city = bdLocation.getCity();
                    savedLocation.district = bdLocation.getDistrict();
                    savedLocation.setOrgLoc(bdLocation);
                    return savedLocation;
                }
            }).subscribeOn(Schedulers.io()).observeOn(Schedulers.io());
        }
    }

    @Override
    public RxLocationManager setOption(final @NonNull ClientOption option) {
        if (option == null) {
            throw new IllegalArgumentException("Option can't be null.");
        }
        clientOption = option;
        mBdlocationClient.setLocOption(option.getBaiduOption());
        return this;
    }

    @SuppressWarnings("PMD.NullAssignment")
    @Override
    public void shutDown() {
        synchronized (BaiduRxLocationManager.class) {
            if (mBdlocationClient != null) {
                if (mBdlocationClient.isStarted()) {
                    mBdlocationClient.stop();
                }
                mBdlocationClient = null;
            }
//            if (null != sINSTANCE) {
//                sINSTANCE = null;
//            }
        }
    }

    final class RxLocationListener implements ObservableSource<BDLocation> {
        private final LocationClient mLocationClient;
        private BDLocationListener mListener;

        RxLocationListener(final LocationClient locationClient) {
            mLocationClient = locationClient;
        }

        @Override
        public void subscribe(final Observer<? super BDLocation> observer) {
            mListener = new BDLocationListener() {
                @Override
                public void onReceiveLocation(final BDLocation bdLocation) {
                    if (mLocationClient.isStarted()) {
                        observer.onNext(bdLocation);
                    }
                }

                @Override
                public void onConnectHotSpotMessage(final String s, final int i) {

                }
            };
            mLocationClient.registerLocationListener(mListener);
            boolean isMain = Thread.currentThread().getName().equals("main");
            if (isMain) {
                throw new RuntimeException("不能运行在主线程");
            }
//            Log.d("zzz CallObservable.subscribeActual ");
            synchronized (RxLocationListener.class) {
                mLocationClient.start();
            }
            observer.onSubscribe(new Disposable() {

                @Override
                public void dispose() {
//                    if (!observer.isUnsubscribed()) {
//                        observer.unsubscribe();
//                    }
                    removeListener();
                }

                @Override
                public boolean isDisposed() {
                    return !mLocationClient.isStarted();
//                    return observer.isUnsubscribed();
                }
            });
        }

        private void removeListener() {
            if (mListener != null) {
//                mLocationClient.registerLocationListener(mListener);
                mLocationClient.unRegisterLocationListener(mListener);
                synchronized (RxLocationListener.class) {
                    mLocationClient.stop();
                }
            }
        }
    }


}
